-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: 管理策略重配的ipmi格式
local class = require 'mc.class'
local json = require 'cjson'
local crc32 = require 'libmgmt_protocol.bios.infrastructure.crc32'
local bs = require 'mc.bitstring'

local IpmiFormat = class()

function IpmiFormat:ctor()
    self.header = {}
    self.data = {}
    self.send_format = '0xDB0700:3/unit:8, 0x23'
    self.receive_format = ''

    self.prop_len = {}
    self.defs = {
        netfn = {
            OemReqNetfn = 0x30
        },
        cmd = {
            ImuCmd = 0x98
        }
    }
end

function IpmiFormat:build_header()
    self.header = {
        DestNetFn = self.defs.netfn.OemReqNetfn,
        Cmd = self.defs.cmd.ImuCmd
    }
end

function IpmiFormat:link_format(format_name, format)
    self[format_name] = string.format('%s, %s', self[format_name], format)
end

function IpmiFormat:build_data(segment)
    self.prop_len[segment.Name] = segment.Size
    if not segment.Default then
        return
    end
    self.data[segment.Name] = segment.Default
end

function IpmiFormat:build_format(format_name, segments)
    local order_keys = json.json_object_get_keys(segments)
    for _, index in pairs(order_keys) do
        local segment = segments[index]
        self:link_format(format_name, string.format('%s:%s/unit:8', segment.Name, segment.Size))
        if format_name == 'send_format' then
            self:build_data(segment)
        end
    end
    self[format_name] = string.format('<<%s>>', self[format_name])
end

function IpmiFormat:parse(function_info)
    self:build_header()
    self:build_format('send_format', function_info.ReqBody)
    self:build_format('receive_format', function_info.RspBody)
end

local function cal_crc(data)
    if not data then
        return 0
    end
    local ok, crc = pcall(function()
        return crc32(data, 0, true)
    end)
    if not ok then
        return 0
    end
    return crc
end

local MANU_LEN = 3
local SUBCMD_LEN = 1
function IpmiFormat:calculate_crc()
    if self.data['Crc32'] then
        local send_data = bs.new(self.send_format):pack(self.data)
        local crc_len = self.prop_len['Crc32']
        local call_id_len = self.prop_len.CallId
        local start_offset = MANU_LEN + SUBCMD_LEN + call_id_len + 1
        local finish_offset = - (crc_len + 1)
        self.data['Crc32'] = cal_crc(string.sub(send_data, start_offset, finish_offset))
    end
end

-- 填充ipmi数据:非关键字的属性，如果用户配置则使用用户的配置，否则使用current
-- config_data:用户配置的数据，current_data:当前值
function IpmiFormat:set_data(config_data, current_data)
    for k, _ in pairs(self.data) do
        if config_data[k] then
            self.data[k] = config_data[k]
        elseif current_data[k] then
            self.data[k] = current_data[k]
        end
    end
    -- 计算crc
    self:calculate_crc()
end

function IpmiFormat:generate_ipmi_msg(system_id)
    local msg = {}
    msg.Header = self.header
    msg.Data = self.data
    msg.SendFormat = self.send_format
    msg.ReceiveFormat = self.receive_format
    msg.SystemId = system_id
    return msg
end

return IpmiFormat